#pragma once
#include <sqlite3.h>
#include <string>
#include<cstring>
#include <sys/stat.h>
#include <sstream>
#include <random>
#include <fstream>
#include <sys/types.h>
#include <stdlib.h>
#include <atomic>
#include <iomanip>
#include <iostream>
#include "mq_logger.hpp"
namespace mymq
{
    class SqliteHelper
    {
    public:
        typedef int (*sql_callback)(void *, int, char **, char **);
        SqliteHelper(std::string path) : _sqlpath(path), _handler(nullptr)
        {
        }
        ~SqliteHelper() {}
        bool Open(int falg = SQLITE_OPEN_FULLMUTEX)
        {
            // 可读可写              不存在则新建       默认串行
            int ret = sqlite3_open_v2(_sqlpath.c_str(), &_handler, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | falg, nullptr);
            if (ret != SQLITE_OK)
            {
                DLOG("创建或打开数据库失败 %s", sqlite3_errmsg(_handler));
                return false;
            }
            return true;
        }
        bool Exec(const std::string &sql, sql_callback cb, void *arg)
        {
            int ret = sqlite3_exec(_handler, sql.c_str(), cb, arg, nullptr);
            if (ret != SQLITE_OK)
            {
                // std::cout << sql << std::endl;
                // std::cout << "执行该语句失败" << std::endl;
                // std::cout << sqlite3_errmsg(_handler) << std::endl;
                DLOG("%s \n执行该语句失败\n %s", sql.c_str(), sqlite3_errmsg(_handler));
                return false;
            }
            return true;
        }
        void Close()
        {
            if (_handler)
                sqlite3_close_v2(_handler);
        }

    private:
        std::string _sqlpath; // 数据库文件名（路径）
        sqlite3 *_handler;
    };

    class StrHelper
    {
    public:
        // 将str以sep为分隔符将该字符串分隔成多个子串保存到result中
        // new...music..pop--->new    music    pop
        static size_t split(const std::string &str, const std::string &sep, std::vector<std::string> &result)
        {
            int pos = 0, idex = 0; // pos分隔符开始位置，ide从哪开始分隔
            while (idex < str.size())
            {
                pos = str.find(sep, idex);
                if (pos == std::string::npos)
                {
                    // 直接将整个字符串添加进容器
                    result.push_back(str.substr(idex, pos - idex));
                    return result.size();
                }
                // 找到第一个分隔符
                // 判断该分隔符位置是否与idex相等，相等说明没有找到有效字符串
                if (pos == idex)
                {
                    idex = pos + sep.size();
                    continue;
                }
                std::string tem = str.substr(idex, pos - idex);
                result.push_back(tem);
                idex = pos + sep.size();
            }
            return result.size();
        }
    };
    class GetUUIDHelper
    {
    public:
        static std::string GetUUID()
        {
            std::random_device dv; // 直接生成一个机器随机数
            // int num = dv();
            std::mt19937_64 gernator(dv()); // 通过梅森旋转算法，生成一个伪随机数
            // 控制范围为0-255
            std::uniform_int_distribution<int> distribution(0, 255);
            // 转化为16进制保存并按照8-4-4格式--->c5cbb153-e132-d884-
            std::stringstream ss;
            for (int i = 0; i < 8; i++)
            {
                ss << std::setw(2) << std::setfill('0') << std::hex << distribution(gernator);
                if (i == 3 || i == 5 || i == 7)
                    ss << "-";
            }
            // 生成八位序号拼接在后边
            static std::atomic<size_t> sep(1); // 从1开始
            size_t tem = sep.fetch_add(1);     // 每次+1
            for (int i = 7; i >= 0; i--)
            {
                ss << std::setw(2) << std::setfill('0') << std::hex << ((tem >> (i * 8)) & 0xff);
                if (i == 6)
                    ss << "-";
            }
            return ss.str();
        }
    };

    class FileHelper
    {
    public:
        FileHelper(const std::string &filename) : _filename(filename) {}

        ~FileHelper() {}
        // 判断该文件是否存在
        bool exists()
        {
            struct stat st;
            return (::stat(_filename.c_str(), &st) == 0);
        }
        size_t size()
        {
            struct stat st;
            // 修正：1.变量名正确拼写 2.函数调用正确 3.处理错误情况
            int ret = ::stat(_filename.c_str(), &st);
            if (ret < 0)
            {
                ELOG("获取文件大小失败: %s", _filename.c_str());
                return 0;
            }
            return static_cast<size_t>(st.st_size);
        }

        // 从哪开始读，要读多少
        bool read(char *body, size_t offset, size_t len)
        {
            // 打开文件
            std::ifstream ifs(_filename, std::ios::binary | std::ios::in);
            if (ifs.is_open() == false)
            {
                ELOG("%s打开文件失败", _filename.c_str());
                return false;
            }
            // 跳转到读写位置
            ifs.seekg(offset, std::ios::beg); // 从文件开始位置跳转offset
            // 读取数据
            ifs.read(body, len); // 读取len长度到body
            if (ifs.good() == false)
            {
                // 读取失败
                ELOG("%s 读取数据失败", _filename.c_str());
                ifs.close();
                return false;
            }
            ifs.close();
            return true;
        }
        // 将读到的数据保存到body
        bool read(std::string &body)
        {
            size_t fsize = this->size();
            body.resize(fsize);
            // 读取整个文件的内容
            return read(&body[0], 0, fsize);
        }

        // 从哪开始写
        bool write(const char *body, size_t offset, size_t len)
        {
            // 打开文件,必须要有读权限才能跳转
            std::fstream fs(_filename, std::ios::binary | std::ios::in |std::ios::out);
            if (fs.is_open() == false)
            {
                ELOG("%s打开文件失败", _filename.c_str());
                return false;
            }
            // 跳转到写的位置
            fs.seekp(offset, std::ios::beg);
            // 写数据
            fs.write(body, len);
            if (fs.good() == false)
            {
                ELOG("%s写入数据到文件失败", _filename.c_str());
                fs.close();
                return false;
            }
            fs.close();
            return true;
        }
        bool write(const std::string &body)
        {
            // 直接从文件开头开始写入，可能会造成数据混乱
            return write(body.c_str(), 0, body.size());
        }
        // 修改文件名
        bool rename(const std::string &nname)
        {
            return (::rename(_filename.c_str(), nname.c_str()) == 0);
        }
        // 创建文件
        static bool createfile(const std::string &filename)
        {
            // 打开文件
            //如果存在就不再创建
            if(FileHelper(filename).exists()==true) return true;
            std::fstream ofs(filename, std::ios::binary | std::ios::out);
            if (ofs.is_open() == false)
            {
                ELOG("%s创建文件失败", filename.c_str());
                return false;
            }
            ofs.close();
            return true;
        }
        // 删除文件
        static bool removefile(const std::string &filename)
        {
            return (::remove(filename.c_str()) == 0);
        }
        // 创建目录，可能是多级目录
        static bool createderectory(const std::string &path)
        {
            int pos, idx = 0;
            while (idx < path.size())
            {
                pos = path.find('/', idx);
                if (pos == std::string::npos)
                {
                    // 只创建单个目录
                    std::string subdir = path.substr(0);
                    return (::mkdir(subdir.c_str(), 0775) == 0);
                }
                std::string substr = path.substr(0, pos);
                int ret = ::mkdir(substr.c_str(), 0775);
                //如果当前目录不存在并且
                if (ret != 0&& errno != EEXIST)
                {

                    ELOG("创建目录 %s 失败: %s\n", path.c_str(),strerror(errno));
                    return false;
                }
                idx = pos + 1;
            }
            return true;
        }
        static bool removederectory(const std::string &path)
        {
            std::string cmd = "rm -rf " + path;
            return (::system(cmd.c_str()) != -1);
        }

        // 获取该文件的父级目录
        static std::string parentderectory(const std::string filename)
        {
            // aaa/bbb/ccc/ code.txt
            size_t pos = filename.find_last_of('/');
            if (pos == std::string::npos)
            {
                // 说明该文件是 code.txt
                return "./";
            }
            return filename.substr(0, pos);
        }

    private:
        std::string _filename;
    };
}
